/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-12-10     xu       the first version
 */
#define DBG_TAG "[hzk16/dbg]"
#define DBG_LVL DBG_LOG
#include "W25Qx.h"
#include "hzk16.h"
#include <fal.h>
#include <rtdbg.h>
#include <rtthread.h>

#ifdef APP_BUILD_HZK16_TO_FIRMWARE

int mem_compare(uint8_t* a, uint8_t* b, uint32_t size);

int hzk16_setup(const char* part_name, uint32_t blk_size)
{
    int retval = 0;

    fal_partition_t part = fal_partition_find(part_name);
    if (part == RT_NULL) {
        rt_kprintf("part %s not found!\n", part_name);
        return -1;
    }

    rt_kprintf("Erasing data in partition %s...\n", part_name);
    retval = fal_partition_erase(part, 0, hzk16_db_size);
    if (retval < 0) {
        rt_kprintf("fal partition erase failed!\n");
        return -1;
    }

    uint8_t* golden = rt_malloc(blk_size);
    rt_kprintf("Writing hzk16 data to partition %s...\n", part_name);
    for (uint32_t i = 0; i < hzk16_db_size; i += blk_size) {
        uint32_t size = (hzk16_db_size - i) < blk_size ? (hzk16_db_size - i) : blk_size;
        rt_memset(golden, 0, size);
        rt_memcpy(golden, &hzk16[i], size);
        retval = fal_partition_write(part, i, golden, size);
        if (retval < 0) {
            rt_kprintf("fal partition write failed!\n");
            retval = -1;
            goto __cleanup;
        }
    }

    uint8_t* buffer = rt_malloc(blk_size);
    rt_kprintf("Validating hzk16 data from partition %s...\n", part_name);
    for (uint32_t i = 0; i < hzk16_db_size; i += blk_size) {
        uint32_t size = (hzk16_db_size - i) < blk_size ? (hzk16_db_size - i) : blk_size;
        rt_memset(buffer, 0, size);
        retval = fal_partition_read(part, i, buffer, size);
        if (retval < 0) {
            rt_kprintf("fal partition read failed!\n");
            retval = -1;
            goto __cleanup;
        }

        rt_memcpy(golden, &hzk16[i], size);
        if (mem_compare(buffer, golden, size) != 0) {
            rt_kprintf("Data mismatch at offset %d!\n", i);
            retval = -1;
            goto __cleanup;
        }
    }

__cleanup:
    rt_free(buffer);
    rt_free(golden);

    rt_kprintf("Write hzk16 data to partition %s done, %d!\n", part_name, retval);
    return retval < 0 ? retval : 0;
}

int hzk16_setup_cmd(int argc, char* argv[])
{
    int retval = 0;
    if (argc < 2) {
        rt_kprintf("%s part_name [batch_size]\n", __func__);
        return -1;
    }

    const char* part_name = argv[1];
    size_t batch_size = argc > 2 ? atoi(argv[2]) : W25Q128FV_PAGE_SIZE;
    if (batch_size < W25Q128FV_PAGE_SIZE) {
        batch_size = W25Q128FV_PAGE_SIZE;
    }
    if (batch_size & (W25Q128FV_PAGE_SIZE - 1)) {
        batch_size -= (batch_size & (W25Q128FV_PAGE_SIZE - 1));
        batch_size += W25Q128FV_PAGE_SIZE;
    }
    rt_kprintf("part_name = %s, batch_size = %d\n", part_name, batch_size);

    if (hzk16_setup(part_name, batch_size)) {
        rt_kprintf("FAILED!\n");
        return -1;
    }
    return 0;
}
MSH_CMD_EXPORT_ALIAS(hzk16_setup_cmd, hzk_setup, setup hzk16 font to flash);

int mem_compare(uint8_t* a, uint8_t* b, uint32_t size)
{
    int retval = 0;
    for (uint32_t i = 0; i < size; i++) {
        if (a[i] != b[i]) {
            rt_kprintf("a, b mismatch at %d: %d, %d\n", i, a[i], b[i]);
            retval = a[i] - b[i];
        }
    }
    return retval;
}

#if 0
#define RAND_SHIFT 37
static uint8_t u8seed = 255;
static uint8_t next = 255;

uint8_t u8srand(uint8_t s)
{
    u8seed = s;
    next = u8seed;
}

uint8_t u8rand()
{
    uint8_t curr = next;
    next = (next + RAND_SHIFT) & 0xFF;
    return next;
}

int part_bench(int argc, char* argv[])
{
    int retval = 0;
    if (argc < 2) {
        rt_kprintf("%s part_name\n", __func__);
        return 1;
    }

    const char* part_name = argv[1];
    fal_partition_t part = fal_partition_find(part_name);
    if (part == RT_NULL) {
        rt_kprintf("part %s not found!\n", part_name);
        return 1;
    }

    rt_kprintf("Erasing data in partition %s...\n", part_name);
    retval = fal_partition_erase(part, 0, hzk16_db_size);
    if (retval < 0) {
        rt_kprintf("fal partition erase failed!\n");
        return 1;
    }

    uint8_t* golden = rt_malloc(W25Q128FV_SECTOR_SIZE);
    uint8_t seed_value = rt_tick_get();
    u8srand(seed_value);

    rt_kprintf("Writing rand data to partition %s...\n", part_name);
    uint8_t* buffer = rt_malloc(W25Q128FV_SECTOR_SIZE);
    for (uint32_t i = 0; i < hzk16_db_size; i += W25Q128FV_SECTOR_SIZE) {
        for (int i = 0; i < W25Q128FV_SECTOR_SIZE; i++) {
            golden[i] = u8rand();
        }

        uint32_t size = (hzk16_db_size - i) < W25Q128FV_SECTOR_SIZE ? (hzk16_db_size - i) : W25Q128FV_SECTOR_SIZE;
        retval = fal_partition_write(part, i, golden, size);
        if (retval < 0) {
            rt_kprintf("fal partition write failed!\n");
            retval = 1;
            break;
        }
    }

    u8srand(seed_value);
    rt_kprintf("Reading random data to partition %s...\n", part_name);
    for (uint32_t i = 0; i < hzk16_db_size; i += W25Q128FV_SECTOR_SIZE) {
        for (int i = 0; i < W25Q128FV_SECTOR_SIZE; i++) {
            golden[i] = u8rand();
        }

        uint32_t size = (hzk16_db_size - i) < W25Q128FV_SECTOR_SIZE ? (hzk16_db_size - i) : W25Q128FV_SECTOR_SIZE;
        rt_memset(buffer, 0, size);
        retval = fal_partition_read(part, i, buffer, size);
        if (retval < 0) {
            rt_kprintf("fal partition read failed!\n");
            retval = 1;
            break;
        }
        if (mem_compare(buffer, golden, size) != 0) {
            rt_kprintf("Data mismatch at offset %d!\n", i);
            retval = 1;
            break;
        }
    }

    rt_free(buffer);
    rt_free(golden);

    rt_kprintf("Benchmark partition %s done!\n", part_name);
    return retval;
}
MSH_CMD_EXPORT(part_bench, hzk16 font setup to flash);
#endif

#endif
